package org.hcf.common.util;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.SystemPropertyUtils;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;

/**
 * 基于 spring PathMatchingResourcePatternResolver封装的class查找类
 *
 * @author zsc
 */

public class ClassResolverUtil {

    public static final String DEFAULT_RESOURCE_PATTERN = "/**/*";

    public static Set<Class<?>> findAnnotated(Class<? extends Annotation> annotation, String packageName) throws ClassNotFoundException, IOException {
        Set<Class<?>> annotatedClasses = new HashSet<>();
        for (Class<?> cls : findClasses(packageName)) {
            if (cls != null && cls.isAnnotationPresent(annotation)) {
                annotatedClasses.add(cls);
            }
        }

        return annotatedClasses;
    }

    @SuppressWarnings("unchecked")
    public static <T> Set<Class<? extends T>> findSubclasses(Class<T> parent, String packageName) throws ClassNotFoundException, IOException {
        Set<Class<? extends T>> subclasses = new HashSet<>();
        for (Class<?> cls : findClasses(packageName)) {
            if (cls != null && parent.isAssignableFrom(cls)) {
                subclasses.add((Class<? extends T>) cls);
            }
        }

        return subclasses;
    }


    public static Set<Class<?>> findClasses(String packageName) throws ClassNotFoundException, IOException {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        String locationPattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                + ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(packageName))
                + DEFAULT_RESOURCE_PATTERN + ClassUtils.CLASS_FILE_SUFFIX;
        MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
        Set<Class<?>> classes = new HashSet<>();

        for (Resource resource : resolver.getResources(locationPattern)) {
            if (resource.isReadable()) {
                MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                if (metadataReader != null) {
                    String className = metadataReader.getClassMetadata().getClassName();
                    classes.add(Class.forName(className));
                }
            }
        }

        return classes;
    }


}
